﻿using Microsoft.Extensions.Options;
using Redis实战.Configurations;
using Redis实战.Interface;
using Redis实战.Models;
using StackExchange.Redis;
using System.Text.Json;

namespace Redis实战.Services
{
    public class RedisCacheService : ICacheService
    {
        private readonly IDatabase _redisDatabase;
        private readonly RedisSettings _redisSettings;
        private readonly ILogger<RedisCacheService> _logger;
        private readonly JsonSerializerOptions _jsonOptions = new()
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
            WriteIndented = false
        };

        public RedisCacheService(
            IConnectionMultiplexer connectionMultiplexer,
            IOptions<RedisSettings> redisSettings,
            ILogger<RedisCacheService> logger)
        {
            _redisDatabase = connectionMultiplexer.GetDatabase();
            _redisSettings = redisSettings.Value;
            _logger = logger;
        }

        private string GetPrefixedKey(string key) => $"{_redisSettings.KeyPrefix}{key}";

        public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
        {
            try
            {
                var redisKey = GetPrefixedKey(key);
                var value = await _redisDatabase.StringGetAsync(redisKey, CommandFlags.None);

                if (value.IsNullOrEmpty)
                {
                    _logger.LogDebug("Cache miss for key: {Key}", key);
                    return default;
                }

                return JsonSerializer.Deserialize<T>(value, _jsonOptions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error retrieving cache for key: {Key}", key);
                return default; // 缓存失败不影响主流程
            }
        }

        public async Task SetAsync<T>(string key, T value, int? expiryMinutes = null, CancellationToken cancellationToken = default)
        {
            if (value == null)
            {
                _logger.LogWarning("Attempted to cache null value for key: {Key}", key);
                return;
            }

            try
            {
                var redisKey = GetPrefixedKey(key);
                var jsonValue = JsonSerializer.Serialize(value, _jsonOptions);
                var expiry = TimeSpan.FromMinutes(expiryMinutes ?? _redisSettings.DefaultExpiryMinutes);

                await _redisDatabase.StringSetAsync(redisKey, jsonValue, expiry, When.Always, CommandFlags.None);
                _logger.LogDebug("Cached value for key: {Key} with expiry: {Expiry}", key, expiry);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error setting cache for key: {Key}", key);
                // 缓存失败不抛出异常，避免影响业务主流程
            }
        }

        public async Task RemoveAsync(string key, CancellationToken cancellationToken = default)
        {
            try
            {
                var redisKey = GetPrefixedKey(key);
                var result = await _redisDatabase.KeyDeleteAsync(redisKey);

                if (result)
                    _logger.LogDebug("Removed cache for key: {Key}", key);
                else
                    _logger.LogDebug("Cache key not found for removal: {Key}", key);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error removing cache for key: {Key}", key);
            }
        }

        public async Task<bool> ExistsAsync(string key, CancellationToken cancellationToken = default)
        {
            try
            {
                var redisKey = GetPrefixedKey(key);
                return await _redisDatabase.KeyExistsAsync(redisKey, CommandFlags.None);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error checking existence for key: {Key}", key);
                return false;
            }
        }
    }
}
